@fieldwangai/agentflow 0.1.48 → 0.1.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1279,6 +1279,108 @@ function readWorkspaceGraph(workspaceRoot) {
1279
1279
  };
1280
1280
  }
1281
1281
 
1282
+ const DISPLAY_SHARE_FILENAME = "display-shares.json";
1283
+
1284
+ function displaySharesPath() {
1285
+ return path.join(getAgentflowDataRoot(), DISPLAY_SHARE_FILENAME);
1286
+ }
1287
+
1288
+ function readDisplayShares() {
1289
+ const file = displaySharesPath();
1290
+ if (!fs.existsSync(file)) return {};
1291
+ const raw = fs.readFileSync(file, "utf-8");
1292
+ if (!raw.trim()) return {};
1293
+ const parsed = JSON.parse(raw);
1294
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1295
+ }
1296
+
1297
+ function writeDisplayShares(shares) {
1298
+ const file = displaySharesPath();
1299
+ fs.mkdirSync(path.dirname(file), { recursive: true });
1300
+ fs.writeFileSync(file, JSON.stringify(shares && typeof shares === "object" ? shares : {}, null, 2) + "\n", "utf-8");
1301
+ }
1302
+
1303
+ function createDisplayShareId() {
1304
+ return crypto.randomBytes(12).toString("base64url");
1305
+ }
1306
+
1307
+ function normalizeDisplayShareNodeIds(ids, graph) {
1308
+ const out = [];
1309
+ const seen = new Set();
1310
+ const instances = graph?.instances && typeof graph.instances === "object" ? graph.instances : {};
1311
+ for (const rawId of Array.isArray(ids) ? ids : []) {
1312
+ const id = String(rawId || "").trim();
1313
+ if (!id || seen.has(id)) continue;
1314
+ const instance = instances[id];
1315
+ if (!workspaceDisplayKind(instance?.definitionId)) continue;
1316
+ seen.add(id);
1317
+ out.push(id);
1318
+ }
1319
+ return out;
1320
+ }
1321
+
1322
+ function publicDisplayPayloadFromShare(root, share) {
1323
+ const scoped = resolveWorkspaceScopeRoot(root, {
1324
+ flowId: share.flowId || "",
1325
+ flowSource: share.flowSource || "user",
1326
+ archived: share.archived === true,
1327
+ }, { userId: share.userId || "" });
1328
+ if (scoped.error) return { error: scoped.error };
1329
+ const { graph } = readWorkspaceGraph(scoped.root);
1330
+ const nodeIds = normalizeDisplayShareNodeIds(share.nodeIds, graph);
1331
+ const instances = graph.instances || {};
1332
+ const displayPage = graph.ui && typeof graph.ui === "object" && graph.ui.displayPage && typeof graph.ui.displayPage === "object"
1333
+ ? graph.ui.displayPage
1334
+ : {};
1335
+ const displayPageSizes = displayPage.nodeSizes && typeof displayPage.nodeSizes === "object" ? displayPage.nodeSizes : {};
1336
+ const displayPagePositions = displayPage.nodePositions && typeof displayPage.nodePositions === "object" ? displayPage.nodePositions : {};
1337
+ const displayPageViewport = displayPage.viewport && typeof displayPage.viewport === "object"
1338
+ ? displayPage.viewport
1339
+ : null;
1340
+ const workspaceSizes = graph.ui && typeof graph.ui === "object" && graph.ui.nodeSizes && typeof graph.ui.nodeSizes === "object"
1341
+ ? graph.ui.nodeSizes
1342
+ : {};
1343
+ const workspacePositions = graph.ui && typeof graph.ui === "object" && graph.ui.nodePositions && typeof graph.ui.nodePositions === "object"
1344
+ ? graph.ui.nodePositions
1345
+ : {};
1346
+ const nodes = nodeIds.map((id) => {
1347
+ const instance = instances[id] || {};
1348
+ const definitionId = String(instance.definitionId || "");
1349
+ return {
1350
+ id,
1351
+ definitionId,
1352
+ kind: workspaceDisplayKind(definitionId),
1353
+ label: String(instance.label || instance.displayName || id),
1354
+ body: String(instance.body || ""),
1355
+ inputs: Array.isArray(instance.input) ? instance.input : [],
1356
+ outputs: Array.isArray(instance.output) ? instance.output : [],
1357
+ size: displayPageSizes[id] || workspaceSizes[id] || null,
1358
+ position: displayPagePositions[id] || workspacePositions[id] || null,
1359
+ };
1360
+ });
1361
+ return {
1362
+ ok: true,
1363
+ share: {
1364
+ id: share.id,
1365
+ title: share.title || "AgentFlow Display",
1366
+ layout: share.layout || "gallery",
1367
+ flowId: share.flowId || "",
1368
+ flowSource: share.flowSource || "user",
1369
+ archived: share.archived === true,
1370
+ nodeIds,
1371
+ viewport: displayPageViewport &&
1372
+ Number.isFinite(Number(displayPageViewport.x)) &&
1373
+ Number.isFinite(Number(displayPageViewport.y)) &&
1374
+ Number.isFinite(Number(displayPageViewport.zoom))
1375
+ ? { x: Number(displayPageViewport.x), y: Number(displayPageViewport.y), zoom: Number(displayPageViewport.zoom) }
1376
+ : null,
1377
+ createdAt: share.createdAt || "",
1378
+ updatedAt: share.updatedAt || "",
1379
+ },
1380
+ nodes,
1381
+ };
1382
+ }
1383
+
1282
1384
  function normalizeWorkspaceGraphPayload(payload) {
1283
1385
  const graph = payload?.graph && typeof payload.graph === "object" ? payload.graph : payload;
1284
1386
  return {
@@ -3139,6 +3241,75 @@ export function startUiServer({
3139
3241
 
3140
3242
  const authUser = getAuthUserFromRequest(req);
3141
3243
  const userCtx = authUser ? { userId: authUser.userId } : {};
3244
+ if (req.method === "GET" && url.pathname === "/api/display/share") {
3245
+ try {
3246
+ const id = String(url.searchParams.get("id") || "").trim();
3247
+ if (!id) {
3248
+ json(res, 400, { error: "Missing display share id" });
3249
+ return;
3250
+ }
3251
+ const share = readDisplayShares()[id];
3252
+ if (!share) {
3253
+ json(res, 404, { error: "Display share not found" });
3254
+ return;
3255
+ }
3256
+ const payload = publicDisplayPayloadFromShare(root, share);
3257
+ if (payload.error) {
3258
+ json(res, 404, { error: payload.error });
3259
+ return;
3260
+ }
3261
+ json(res, 200, payload);
3262
+ } catch (e) {
3263
+ json(res, 500, { error: (e && e.message) || String(e) });
3264
+ }
3265
+ return;
3266
+ }
3267
+
3268
+ if (req.method === "GET" && url.pathname === "/api/display/file/raw") {
3269
+ try {
3270
+ const id = String(url.searchParams.get("id") || "").trim();
3271
+ if (!id) {
3272
+ json(res, 400, { error: "Missing display share id" });
3273
+ return;
3274
+ }
3275
+ const share = readDisplayShares()[id];
3276
+ if (!share) {
3277
+ json(res, 404, { error: "Display share not found" });
3278
+ return;
3279
+ }
3280
+ const scoped = resolveWorkspaceScopeRoot(root, {
3281
+ flowId: share.flowId || "",
3282
+ flowSource: share.flowSource || "user",
3283
+ archived: share.archived === true,
3284
+ }, { userId: share.userId || "" });
3285
+ if (scoped.error) {
3286
+ json(res, 404, { error: scoped.error });
3287
+ return;
3288
+ }
3289
+ const { abs, rel } = resolveWorkspaceFilePath(scoped.root, url.searchParams.get("path") || "");
3290
+ if (!fs.existsSync(abs) || !fs.statSync(abs).isFile()) {
3291
+ json(res, 404, { error: "File not found" });
3292
+ return;
3293
+ }
3294
+ const ext = path.extname(abs).toLowerCase();
3295
+ const type = MIME[ext] || "application/octet-stream";
3296
+ const data = fs.readFileSync(abs);
3297
+ const headers = {
3298
+ "Content-Type": type,
3299
+ "Content-Length": data.length,
3300
+ "Cache-Control": "public, max-age=300",
3301
+ };
3302
+ if (url.searchParams.get("download") === "1") {
3303
+ headers["Content-Disposition"] = workspaceDownloadContentDisposition(rel);
3304
+ }
3305
+ res.writeHead(200, headers);
3306
+ res.end(data);
3307
+ } catch (e) {
3308
+ json(res, /traversal/i.test(String(e.message || e)) ? 403 : 500, { error: (e && e.message) || String(e) });
3309
+ }
3310
+ return;
3311
+ }
3312
+
3142
3313
  if (url.pathname.startsWith("/api/") && !authUser) {
3143
3314
  json(res, 401, { error: "Authentication required", setupRequired: authSetupRequired() });
3144
3315
  return;
@@ -3511,6 +3682,55 @@ export function startUiServer({
3511
3682
  return;
3512
3683
  }
3513
3684
 
3685
+ if (req.method === "POST" && url.pathname === "/api/display/share") {
3686
+ let payload;
3687
+ try {
3688
+ payload = JSON.parse(await readBody(req));
3689
+ } catch {
3690
+ json(res, 400, { error: "Invalid JSON body" });
3691
+ return;
3692
+ }
3693
+ try {
3694
+ const scoped = resolveWorkspaceScopeRoot(root, {
3695
+ flowId: payload.flowId || "",
3696
+ flowSource: payload.flowSource || "user",
3697
+ archived: payload.archived === true || payload.flowArchived === true,
3698
+ }, userCtx);
3699
+ if (scoped.error) {
3700
+ json(res, 400, { error: scoped.error });
3701
+ return;
3702
+ }
3703
+ const { graph } = readWorkspaceGraph(scoped.root);
3704
+ const nodeIds = normalizeDisplayShareNodeIds(payload.nodeIds, graph);
3705
+ if (nodeIds.length === 0) {
3706
+ json(res, 400, { error: "请选择至少一个 display 节点" });
3707
+ return;
3708
+ }
3709
+ const shares = readDisplayShares();
3710
+ let id = createDisplayShareId();
3711
+ while (shares[id]) id = createDisplayShareId();
3712
+ const now = new Date().toISOString();
3713
+ const share = {
3714
+ id,
3715
+ userId: authUser.userId,
3716
+ flowId: scoped.flowId || "",
3717
+ flowSource: scoped.flowSource || "user",
3718
+ archived: scoped.archived === true,
3719
+ title: String(payload.title || "").trim() || "AgentFlow Display",
3720
+ layout: ["canvas", "gallery", "slides", "document"].includes(String(payload.layout || "")) ? String(payload.layout) : "canvas",
3721
+ nodeIds,
3722
+ createdAt: now,
3723
+ updatedAt: now,
3724
+ };
3725
+ shares[id] = share;
3726
+ writeDisplayShares(shares);
3727
+ json(res, 200, { ok: true, share, url: `/display/${encodeURIComponent(id)}` });
3728
+ } catch (e) {
3729
+ json(res, 500, { error: (e && e.message) || String(e) });
3730
+ }
3731
+ return;
3732
+ }
3733
+
3514
3734
  if (req.method === "POST" && url.pathname === "/api/workspace/graph") {
3515
3735
  let payload;
3516
3736
  try {